<?php
/**
 *    This file is part of "Liberty Gaming NQ Stats Analyzer".
 *
 *    "Liberty Gaming NQ Stats Analyzer" is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    "Liberty Gaming NQ Stats Analyzer" is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


/**
 * Class PCPIN_DB
 * Manage database operations.
 * @author Konstantin Reznichak <k.reznichak@pcpin.com>
 * @copyright Copyright &copy; 2007, Konstantin Reznichak
 */
class PCPIN_DB {

  /**
   * Database connection handler
   * @var   resource
   */
  var $_db_conn=null;

  /**
   * Array with listed rows
   * @var   array
   */
  var $_db_list=null;

  /**
   * Elements count in $_db_list array
   * @var   int
   */
  var $_db_list_count=0;


  /**
   * Constructor.
   * Connect to database.
   * @param   object  &$caller        Caller object
   * @param   array   $db_conndata    Database connection data
   */
  function PCPIN_DB(&$caller, $db_conndata) {
    // Query args separator. DO NOT CHANGE!!!
    if (!defined('PCPIN_SQLQUERY_ARG_SEPARATOR_START')) define('PCPIN_SQLQUERY_ARG_SEPARATOR_START', chr(0).chr(255).PCPIN_Common::randomString(10));
    // Query args separator. DO NOT CHANGE!!!
    if (!defined('PCPIN_SQLQUERY_ARG_SEPARATOR_END')) define('PCPIN_SQLQUERY_ARG_SEPARATOR_END', chr(255).chr(0).PCPIN_Common::randomString(10));
    // Connect to database
    if (empty($this->_db_conn)) {
      if (!function_exists('mysql_connect')) {
        // MySQL extension is not loaded
        die('<b>Fatal error</b>: MySQL extension is not loaded');
      } elseif (!$this->_db_conn=mysql_connect($db_conndata['server'], $db_conndata['user'], $db_conndata['password'])) {
        // Failed to connect database server
        die('<b>Fatal error</b>: Failed to connect database server');
      } else {
        // Database server connected
        $this->_db_query('SET NAMES "utf8"');
        $this->_db_query('SET SESSION sql_mode=""');
        // Check MySQL server version
        $mysql_version=$this->_db_mysql_version();
        $mysql_exists=explode('.', $mysql_version);
        $mysql_needed=explode('.', PCPIN_REQUIRESMYSQL);
        foreach ($mysql_needed as $key=>$val) {
          if (!isset($mysql_exists[$key])) {
            // Installed MySQL version is OK
            break;
          } else {
            if ($val>$mysql_exists[$key]) {
              // MySQL version is too old
              die('<b>Fatal error</b>: Installed MySQL server version is <b>'.$mysql_version.'</b> (minimum required MySQL version is <b>'.PCPIN_REQUIRESMYSQL.'</b>)');
            } elseif ($val<$mysql_exists[$key]) {
              // Installed MySQL version is OK
              break;
            }
          }
        }
        // Trying do select database
        if (!mysql_select_db($db_conndata['database'], $this->_db_conn)) {
          // Failed to select database
          die('<b>Fatal error</b>: Failed to select database');
          $this->_db_close();
        } else {
          // Define database table names prefix
          if (!defined('PCPIN_DB_PREFIX')) define('PCPIN_DB_PREFIX', $db_conndata['tbl_prefix']);
        }
      }
    }
    unset($db_conndata);
    $this->_cache['_db_tabledata']=array(); // Cached table information ($this->_cache is a property of the parent class)
    $this->_db_pass_vars($this, $caller);
  }


  /**
   * Free result memory
   * @param   resource    &$result    Result identifier
   */
  function _db_freeResult(&$result) {
    if (is_resource($result)) {
      mysql_free_result($result);
    }
  }


  /**
   * Close database connection
   */
  function _db_close() {
    if (is_resource($this->_db_conn)) {
      mysql_close($this->_db_conn);
    }
    $this->_db_conn=null;
  }


  /**
   * Detect MySQL server software version
   * @return  mixed   Server software version string or false on error
   */
  function _db_mysql_version() {
    $version=false;
    if (is_resource($this->_db_conn)) {
      $result=$this->_db_query('SELECT VERSION()');
      if ($data=$this->_db_fetch($result, MYSQL_NUM)) {
        $version=$data[0];
      }
      $this->_db_freeResult($result);
    }
    return $version;
  }


  /**
   * Execute SQL query and return resulted resource
   * @param   string    $query        Query to execute
   * @param   boolean   $unbuffered   If TRUE, then query will be executed in unbuffered mode
   *                                  (without fetching and buffering the result rows). USE WITH CARE!!!
   * @return  mixed   Query execution resource or false on error
   */
  function _db_query($query='', $unbuffered=false) {
    // If $show_slowest>0 then all queries with execution time >= $show_slowest
    // will be monitored.
    $show_slowest=0;
    $result=false;
    if (!empty($query)) {
      if (true!==$unbuffered) {
        // Buffered query. Default.
        $result=mysql_query($query, $this->_db_conn);
      } else {
        // Unbuffered query. Use with care!
        $result=mysql_unbuffered_query($query, $this->_db_conn);
      }
#echo "$query\n\n";
    }
    return $result;
  }


  /**
   * Fetch a query execution result row as an associative array, a numeric array, or both.
   * @param   resource  $result       Query execution result
   * @param   int       $result_type  The type of array that is to be fetched
   * @return  mixed   Fetched array or false if no mere data present
   */
  function _db_fetch($result, $result_type=MYSQL_ASSOC) {
    $data=false;
    if (is_resource($result)) {
      // Determine field types
      $field_types=array();
      $i=0;
      $num_fields=mysql_num_fields($result);
      while ($i<$num_fields) {
        $meta=mysql_fetch_field($result, $i);
        $name=$meta->name;
        $type=strtolower($meta->type);
        // Map data types
        if ($type=='string') {
          $type='';
        } elseif (false!==strpos($type, 'int')) {
          $type='int';
        } elseif (   false!==strpos($type, 'dec')
                  || false!==strpos($type, 'float')
                  || false!==strpos($type, 'real')
                  || false!==strpos($type, 'double')) {
          $type='float';
        } else {
          $type='';
        }
        switch($result_type) {
          case MYSQL_ASSOC  :
          default           :   $field_types[$name]=$type;
                                break;
          case MYSQL_NUM    :   $field_types[$i]=$type;
                                break;
          case MYSQL_BOTH   :   $field_types[$name]=$type;
                                $field_types[$i]=$type;
                                break;
        }
        $i++;
      }
      $magic_quotes_runtime=get_magic_quotes_runtime();
      // Disable magic_quotes_runtime
      @set_magic_quotes_runtime(0);
      $data=mysql_fetch_array($result, $result_type);
      // Restore an original magic_quotes_runtime setting
      @set_magic_quotes_runtime($magic_quotes_runtime);
      // Cast types
      foreach ($field_types as $key=>$val) {
        if (!is_null($data[$key]) && $val!='') {
          settype($data[$key], $val);
        }
      }
    }
    return $data;
  }


  /**
   * Escapes special characters in a string for use in a SQL statement.
   * WARNING! Always use this methode for creating queries!
   * @param   string    $string             String to escape
   * @param   boolean   $escape_wildcards   If TRUE, then wildcard chars "%" and "_" will be also escaped
   * @return  string  Escaped string
   */
  function _db_escapeStr($string='', $escape_wildcards=true) {
    $result='';
    if (false===$result=mysql_real_escape_string($string, $this->_db_conn)) {
      // An error occured
      $result='';
    } elseif ($escape_wildcards) {
      $result=str_replace('_', '\\_', str_replace('%', '\\%', $result));
    }
    return $result;
  }


  /**
   * Load databse table row into an object
   * @param   mixed     $id         Table row ID field value
   * @param   string    $id_field   Table row ID field name
   * @return  boolean   TRUE on success or FALSE on error. If an error occured,
   *                    then object property with name specified by $id_field will be set to null
   */
  function _db_loadObj($id='', $id_field='id') {
    $ok=false;
    if (!empty($id_field)) {
      // Read record from database
      if ($this->_db_getList($id_field.' =# '.$id, 1)) {
        // Record found
        $ok=true;
        $this->_db_setObject($this->_db_list[0]);
      }
    }
    if (!$ok) {
      // An error occured
      if (!empty($id_field) && isset($this->$id_field)) {
        $this->$id_field=null;
      }
    }
    return $ok;
  }


  /**
   * Apply associative array to the current object
   * @param   array   $data     Data to apply
   */
  function _db_setObject($data) {
    if (!empty($data) && is_array($data)) {
      foreach ($data as $key=>$val) {
        if ((is_scalar($val) || is_null($val)) && (isset($this->$key) || is_null($this->$key))) {
          $this->$key=$val;
        }
      }
    }
  }


  /**
   * Store object properties into assotiative array
   * @return  array
   */
  function _db_getFromObject() {
    $data=array();
    $this_class=get_class($this);
    $this_vars=array_keys(get_class_vars($this_class));
    $parent_class=get_parent_class(get_class($this));
    if (!empty($parent_class)) {
      $parent_vars=get_class_vars($parent_class);
    } else {
      $parent_vars=array();
    }
    foreach ($this_vars as $var) {
      if (!array_key_exists($var, $parent_vars)) {
        $data[$var]=$this->$var;
      }
    }
    return $data;
  }


  /**
   * Updates databse table row with object's data. ID field will not be updated.
   * @param   mixed     $id         Table row ID field value
   * @param   string    $id_field   Table row ID field name
   * @return  boolean   TRUE on success or FALSE on error
   */
  function _db_updateObj($id='', $id_field='id') {
    $ok=false;
    $tbl=$this->_db_getTbl();
    if (!empty($tbl) && !empty($id_field)) {
      // Get table information
      $tabledata=$this->_db_tableFields($tbl);
      if (!empty($tabledata)) {
        $parts=array();
        foreach ($tabledata as $field) {
          if (is_null($this->$field['Field']) || isset($this->$field['Field']) && $field['Field']!=$id_field) {
            if (is_null($this->$field['Field'])) {
              $parts[]='`'.$field['Field'].'` = NULL';
            } elseif ($this->$field['Field']=='' && array_key_exists('Default', $field)) {
              $parts[]='`'.$field['Field'].'` = "'.$this->_db_escapeStr($field['Default'], false).'"';
            } else {
              $parts[]='`'.$field['Field'].'` = "'.$this->_db_escapeStr($this->$field['Field'], false).'"';
            }
          }
        }
        if (!empty($parts)) {
          // Create and execute query
          $query='UPDATE `'.$tbl.'` SET '.implode(', ', $parts).' WHERE `'.$id_field.'` = BINARY "'.$this->_db_escapeStr($id, false).'" LIMIT 1';
          if ($result=$this->_db_query($query)) {
            $this->_db_freeResult($result);
            $ok=true;
          }
        }
      }
    }
    return $ok;
  }


  /**
   * Updates databse table row using suplied data. ID field will not be updated.
   * @param   mixed     $id         Table row ID field value
   * @param   string    $id_field   Table row ID field name
   * @param   array     $data       Data to apply as array with field name as KEY and field value as VAL
   * @return  boolean   TRUE on success or FALSE on error
   */
  function _db_updateRow($id='', $id_field='id', $data=null) {
    $ok=false;
    $tbl=$this->_db_getTbl();
    if (!empty($tbl) && !empty($id_field) && !empty($data) && is_array($data)) {
      // Get table information
      $tabledata=$this->_db_tableFields($tbl);
      if (!empty($tabledata)) {
        $parts=array();
        foreach ($tabledata as $field) {
          if (array_key_exists($field['Field'], $data) && (is_scalar($data[$field['Field']]) || is_null($data[$field['Field']]))) {
            if (is_null($data[$field['Field']])) {
              $parts[]='`'.$field['Field'].'` = NULL';
            } elseif ($data[$field['Field']]=='' && array_key_exists('Default', $field)) {
              $parts[]='`'.$field['Field'].'` = "'.$this->_db_escapeStr($field['Default'], false).'"';
            } else {
              $parts[]='`'.$field['Field'].'` = "'.$this->_db_escapeStr($data[$field['Field']], false).'"';
            }
          }
        }
        if (!empty($parts)) {
          // Create and execute query
          $query='UPDATE `'.$tbl.'` SET '.implode(', ', $parts).' WHERE `'.$id_field.'` = BINARY "'.$this->_db_escapeStr($id, false).'" LIMIT 1';
          if ($result=$this->_db_query($query)) {
            $this->_db_freeResult($result);
            $ok=true;
          }
        }
      }
    }
    return $ok;
  }


  /**
   * Delete a row from the databse table
   * @param   mixed     $id         Table row ID field value
   * @param   string    $id_field   Table row ID field name
   * @param   boolean   $no_limit   If TRUE, then no LIMIT will be used in query, otherwise: LIMIT 1
   * @return  boolean   TRUE on success or FALSE on error
   */
  function _db_deleteRow($id='', $id_field='id', $no_limit=false) {
    $ok=false;
    $tbl=$this->_db_getTbl();
    if (!empty($tbl) && !empty($id_field)) {
      // Get table information
      $tabledata=$this->_db_tableFields($tbl);
      foreach ($tabledata as $field) {
        if ($field['Field']==$id_field) {
          // Create and execute query
          $limit=($no_limit)? '' : ' LIMIT 1';
          $query='DELETE FROM `'.$tbl.'` WHERE `'.$id_field.'` = BINARY "'.$this->_db_escapeStr($id, false).'" '.$limit;
          if ($result=$this->_db_query($query)) {
            $this->_db_freeResult($result);
            $ok=true;
          }
          break;
        }
      }
    }
    return $ok;
  }


  /**
   * Delete a row from the databse table using multiple `<field>` [= BINARY "<val>" | IS NULL] 'AND' arguments
   * @param   array     $cond       An array with multiple WHERE conditions (field name as KEY and value as VAL)
   * @param   boolean   $no_limit   If TRUE, then no LIMIT will be used in query, otherwize: LIMIT 1
   * @return  boolean   TRUE on success or FALSE on error
   */
  function _db_deleteRowMultiCond($cond=null, $no_limit=false) {
    $ok=false;
    $cond_ok=true;
    $tbl=$this->_db_getTbl();
    if (!empty($tbl) && !empty($cond) && is_array($cond)) {
      // Get table information
      $tabledata=$this->_db_tableFields($tbl);
      $where=array();
      foreach ($tabledata as $field) {
        if (array_key_exists($field['Field'], $cond)) {
          if (is_null($cond[$field['Field']])) {
            $where[]='`'.$field['Field'].'` IS NULL';
          } elseif (is_scalar($cond[$field['Field']])) {
            $where[]='`'.$field['Field'].'` = BINARY "'.$this->_db_escapeStr($cond[$field['Field']], false).'"';
          } else {
            $cond_ok=false;
            break;
          }
        }
      }
      if ($cond_ok && !empty($where)) {
        // Create and execute query
        $limit=($no_limit)? '' : ' LIMIT 1';
        $query='DELETE FROM `'.$tbl.'` WHERE '.implode(' AND ', $where).' '.$limit;
        if ($result=$this->_db_query($query)) {
          $this->_db_freeResult($result);
          $ok=true;
        }
      }
    }
    return $ok;
  }


  /**
   * Insert a row into the databse table using object's data
   * @return  boolean   TRUE on success or FALSE on error
   */
  function _db_insertObj() {
    $ok=false;
    // Get table information
    $tbl=$this->_db_getTbl();
    $tabledata=$this->_db_tableFields($tbl);
    if (!empty($tabledata)) {
      $fields=array();
      $values=array();
      foreach ($tabledata as $field) {
        if (isset($this->$field['Field'])) {
          $fields[]='`'.$field['Field'].'`';
          $values[]=(is_null($this->$field['Field']))? 'NULL' : '"'.$this->_db_escapeStr($this->$field['Field'], false).'"';
        }
      }
      if (!empty($fields)) {
        // Create and execute query
        $query='INSERT INTO `'.$tbl.'` ('.implode(', ', $fields).') VALUES ('.implode(', ', $values).')';
        if ($result=$this->_db_query($query)) {
          $this->_db_freeResult($result);
          $ok=true;
        }
      }
    }
    return $ok;
  }


  /**
   * List MySQL table fields
   * @param    string   $tbl_name   Database table name
   * @return   mixed  Array with table fields as returned by PHP's mysql_list_fields()
   *                  function or false on error
   */
  function _db_tableFields($tbl_name='') {
    $fields=false;
    if ($tbl_name!='') {
      // Get table information
      if (!isset($this->_cache['_db_tabledata'][$tbl_name])) {
        // Table fields are not read yet. Do it.
        if ($result=$this->_db_query('SHOW COLUMNS FROM `'.$tbl_name.'`')) {
          $table=array();
          while ($data=$this->_db_fetch($result, MYSQL_ASSOC)) {
            $table[]=$data;
          }
          $this->_db_freeResult($result);
          if (!empty($table)) {
            // Store table fields data into cache
            $this->_cache['_db_tabledata'][$tbl_name]=$table;
            $fields=$this->_cache['_db_tabledata'][$tbl_name];
          }
        }
      } else {
        // Get table fields from cache
        $fields=$this->_cache['_db_tabledata'][$tbl_name];
      }
    }
    return $fields;
  }


 /**
  * List rows of single table into object property $_db_list using simple filter.
  * Listed rows will be saved into $this->_db_list array and their count will be saved
  * into $this->_db_list_count property.
  * @param    mixed   ...  Unlimited query parameters
  * @return   int     Listed rows count
  */
  function _db_getList() {
    $this->_db_freeList();
    $tbl=$this->_db_getTbl();
    if (!empty($tbl)) {
      $count=false;
      $select='*';
      $param=array();
      $where='';
      $order=array();
      $orderby='';
      $limitstart=null;
      $limitlength=null;
      $limit='';
      // Get function arguments
      $argv=func_get_args();
      // Process arguments
      foreach ($argv as $arg_key=>$arg) {
        if ($arg_key==0 && $arg=='COUNT') {
          // There will be a COUNT(*) query. An empty array will be saved into _db_list property
          // and the count will be saved into _db_list_count
          $count=true;
          $select='COUNT(*)';
        } else {
          $pos=array('>=#'     =>  strpos($arg, '>=#'),
                     '>='      =>  strpos($arg, '>='),
                     '<=#'     =>  strpos($arg, '<=#'),
                     '<='      =>  strpos($arg, '<='),
                     '!=#'     =>  strpos($arg, '!=#'),
                     '!='      =>  strpos($arg, '!='),
                     '>#'      =>  strpos($arg, '>#'),
                     '>'       =>  strpos($arg, '>'),
                     '<#'      =>  strpos($arg, '<#'),
                     '<'       =>  strpos($arg, '<'),
                     '=#'      =>  strpos($arg, '=#'),
                     '='       =>  strpos($arg, '='),
                     '!NULL'   =>  strpos($arg, '!NULL'),
                     'NULL'    =>  strpos($arg, 'NULL'),
                     '!_LIKE#' =>  strpos($arg, '!_LIKE#'),
                     '!LIKE#' =>  strpos($arg, '!LIKE#'),
                     '_LIKE#'  =>  strpos($arg, '_LIKE#'),
                     'LIKE#'  =>  strpos($arg, 'LIKE#'),
                     '!_LIKE'   =>  strpos($arg, '!_LIKE'),
                     '!LIKE'   =>  strpos($arg, '!LIKE'),
                     '_LIKE'    =>  strpos($arg, '_LIKE'),
                     'LIKE'    =>  strpos($arg, 'LIKE'),
                     'IN'      =>  strpos($arg, 'IN'),
                     '!IN'     =>  strpos($arg, '!IN')
                     );
          // Looking for the first comparison sign (longest signs first)
          asort($pos);
          $pos_=array();
          $val_start=null;
          foreach ($pos as $key=>$val) {
            if (false!==($val)) {
              if (is_null($val_start) || $val==$val_start) {
                $val_start=$val;
                $pos_[$key]=strlen($key);
              } else {
                break;
              }
            }
          }
          if ($arg_key==0 && empty($pos_) && false===strpos($arg, ' ASC') && false===strpos($arg, ' DESC') && false===pcpin_ctype_digit($arg)) {
            // Only specified fields will be selected
            $select_array=explode(',', $arg);
            $select_arr=array();
            foreach ($select_array as $key=>$val) {
              $val=trim(str_replace('`', '', $val));
              if ($val!='') {
                if (substr($val, 0, 2)!='x0') {
                  // Select values "as is"
                  $select_arr[$key]='`'.$val.'`';
                } else {
                  // Select HEX-encoded values
                  $val=substr($val, 2);
                  $select_arr[$key]='HEX(`'.$val.'`) AS `'.$val.'`';
                }
              }
            }
            $select=!empty($select_arr)? implode(',', $select_arr) : '*';
            unset($select_arr);
            unset($select_array);
          } elseif (!empty($pos_)) {
            // Argument contains two parts
            arsort($pos_);
            reset($pos_);
            $key=key($pos_);
            $val=$val_start;
            $part1=trim(substr($arg, 0, $val));
            $key_length=strlen($key);
            switch ($key) {
              case 'NULL':
                // 'IS NULL' operator
                $param[]='`'.$part1.'` IS NULL';
              break;
              case '!NULL':
                // 'IS NOT NULL' operator
                $param[]='`'.$part1.'` IS NOT NULL';
              break;
              case 'LIKE':
              case '_LIKE':
                // 'LIKE' operator with or without escaped wildcards
                $part2=(substr($arg, $val+$key_length, 1)==' ')? substr($arg, $val+$key_length+1) : substr($arg, $val+$key_length);
                $param[]='`'.$part1.'` LIKE "'.$this->_db_escapeStr($part2, '_'!=substr($key, 0, 1)).'"';
              break;
              case 'LIKE#':
              case '_LIKE#':
                // 'LIKE BINARY' operator with or without escaped wildcards
                $part2=(substr($arg, $val+$key_length, 1)==' ')? substr($arg, $val+$key_length+1) : substr($arg, $val+$key_length);
                $param[]='`'.$part1.'` LIKE BINARY "'.$this->_db_escapeStr($part2, '_'!=substr($key, 0, 1)).'"';
              break;
              case '!LIKE':
              case '!_LIKE':
                // 'NOT LIKE' operator with or without escaped wildcards
                $part2=(substr($arg, $val+$key_length, 1)==' ')? substr($arg, $val+$key_length+1) : substr($arg, $val+$key_length);
                $param[]='`'.$part1.'` NOT LIKE "'.$this->_db_escapeStr($part2, '_'!=substr($key, 0, 1)).'"';
              break;
              case '!LIKE#':
              case '!_LIKE#':
                // 'NOT LIKE BINARY' operator with or without escaped wildcards
                $part2=(substr($arg, $val+$key_length, 1)==' ')? substr($arg, $val+$key_length+1) : substr($arg, $val+$key_length);
                $param[]='`'.$part1.'` NOT LIKE BINARY "'.$this->_db_escapeStr($part2, '_'!=substr($key, 0, 1)).'"';
              break;
              case 'IN':
                // 'IN()' operator
                $part2=(substr($arg, $val+$key_length, 1)==' ')? substr($arg, $val+$key_length+1) : substr($arg, $val+$key_length);
                $this->_db_prepareList($part2);
                $param[]='`'.$part1.'` IN('.$part2.')';
              break;
              case '!IN':
                // 'NOT IN()' operator
                $part2=(substr($arg, $val+$key_length, 1)==' ')? substr($arg, $val+$key_length+1) : substr($arg, $val+$key_length);
                $this->_db_prepareList($part2);
                $param[]='`'.$part1.'` IN('.$part2.')';
              break;
              case '>'  :
              case '<'  :
              case '>=' :
              case '<=' :
              case '='  :
              case '!=' :
              case '>#'  :
              case '<#'  :
              case '>=#' :
              case '<=#' :
              case '=#'  :
              case '!=#' :
                // '>' '<' '>=' '<=' '=' '!=' [BINARY] operators
                $part2=(substr($arg, $val+$key_length, 1)==' ')? substr($arg, $val+$key_length+1) : substr($arg, $val+$key_length);
                $key=str_replace('#', ' BINARY', $key);
                $param[]='`'.$part1.'` '.$key.' "'.$this->_db_escapeStr($part2, false).'"';
              break;
            }
          } else {
            // Argument contains one part only (can be 'ORDER BY' or 'LIMIT')
            $arg=trim($arg);
            if (pcpin_ctype_digit($arg)) {
              // Argument contains digits only (one of LIMIT arguments)
              if (is_null($limitlength)) {
                $limitlength=$arg;
              } elseif (is_null($limitstart)) {
                $limitstart=$limitlength;
                $limitlength=$arg;
              }
            } else {
              // ORDER BY parameter
              while (false!==strpos($arg, '  ')) {
                $arg=str_replace('  ', ' ', $arg);
              }
              $parts=explode(' ', $arg);
              if (isset($parts[1]) && strtolower(trim($parts[1]))=='desc') {
                // DESC order
                $order[]='`'.trim($parts[0]).'` DESC';
              } else {
                // ASC order
                $order[]='`'.trim($parts[0]).'` ASC';
              }
            }
          }
        }
      }
      // Prepare WHERE, ORDER BY and LIMIT arguments
      $where=(!empty($param))? (' WHERE '.implode(' AND ', $param)) : '';
      $orderby=(!empty($order))? (' ORDER BY '.implode(', ', $order)) : '';
      $limit=(!is_null($limitstart))? (" LIMIT $limitstart, $limitlength") : ((!is_null($limitlength))? (" LIMIT $limitlength") : '');
      // Create query
      $query='SELECT '.$select.' FROM `'.$tbl.'` '.$where.$orderby.$limit;
#echo $query." <br />\n";
      $result=$this->_db_query($query);
      if ($count) {
        if ($data=$this->_db_fetch($result, MYSQL_NUM)) {
          $this->_db_list_count=$data[0];
        }
        $this->_db_freeResult($result);
      } else {
        while ($data=$this->_db_fetch($result, MYSQL_ASSOC)) {
          $this->_db_list[]=$data;
          $this->_db_list_count++;
        }
        $this->_db_freeResult($result);
      }
    }
    return $this->_db_list_count;
  }


 /**
  * Clear loaded list and list count
  */
  function _db_freeList() {
    $this->_db_list=array();
    $this->_db_list_count=0;
  }


 /**
  * Get the unique ID for the last inserted row
  * @return   int
  */
  function _db_lastInsertID() {
    $last_id=0;
    $result=$this->_db_query('SELECT LAST_INSERT_ID()');
    if ($data=$this->_db_fetch($result, MYSQL_NUM)) {
      $last_id=$data[0];
    }
    $this->_db_freeResult($result);
    return $last_id;
  }


 /**
  * Get database tables list
  * @return   array  Array with table names
  */
  function _db_listTables() {
    $tables=array();
    $query=$this->_db_makeQuery(113);
    if ($result=$this->_db_query($query)) {
      while ($data=$this->_db_fetch($result, MYSQL_NUM)) {
        $tables[]=$data[0];
      }
      $this->_db_freeResult($result);
    }
    return $tables;
  }


 /**
  * Get database table information
  * @param    string    $table    Table name
  * @return   array  Array with table information
  */
  function _db_readTableData($table='') {
    $info=array();
    if ($table!='') {
      $query=$this->_db_makeQuery(114, $table);
      if ($result=$this->_db_query($query)) {
        while ($data=$this->_db_fetch($result)) {
          $info[]=$data;
        }
        $this->_db_freeResult($result);
      }
    }
    return $info;
  }


 /**
  * Get database table indexes
  * @param    string    $table    Table name
  * @return   array  Array with table indexes
  */
  function _db_readTableIndexes($table='') {
    $indexes=array();
    if ($table!='') {
      $query=$this->_db_makeQuery(115, $table);
      if ($result=$this->_db_query($query)) {
        while ($data=$this->_db_fetch($result)) {
          $indexes[]=$data;
        }
        $this->_db_freeResult($result);
      }
    }
    return $indexes;
  }


 /**
  * Prepare list of values to be used by MySQL IN() operator
  * @param    string    &$list            List string
  * @param    boolean   $string_elements  List contains string elements
  */
  function _db_prepareList(&$list, $string_elements=false) {
    if ($list!='') {
      if (!$string_elements) {
        // List may contain digits only
        if (!pcpin_ctype_digit(str_replace(' ', '', str_replace(',', '', $list)))) {
          // ERROR: List contains string elements
          $list='';
        }
      } else {
        // List may contain string elements
        $list=explode(',', $list);
        foreach ($list as $key=>$val) {
          // Escaping string list elements
          $list[$key]='"'.$this->_db_escapeStr($val, false).'"';
        }
        $list=implode(',', $list);
      }
    }
  }


  /**
   * Get database table name
   * @return  string
   */
  function _db_getTbl() {
    return PCPIN_DB_PREFIX.strtolower(substr(get_class($this), 6)); // "PCPIN_" prefix removed
  }


  /**
   * Reference properties' values from one object to another
   * @param   object    $src_obj    Object to pass variables from
   * @param   object    $tgt_obj    Object to pass variables to
   */
  function _db_pass_vars(&$src_obj, &$tgt_obj) {
    if (is_object($tgt_obj)) {
      // Get source vars
      $src_vars=get_object_vars($src_obj);
      foreach ($src_vars as $key=>$val) {
        if ($key!='_db_list' && $key!='_db_list_count' && '_'==substr($key, 0, 1) && '_s_'!=substr($key, 0, 2)) {
          $tgt_obj->$key=&$src_obj->$key;
        }
      }
    }
  }


 /**
  * Check and repair/optimize database tables
  */
  function _db_cure() {
    $tables=$this->_db_listTables();
    foreach ($tables as $table) {
      // Check table
      if ($result=$this->_db_query($this->_db_makeQuery(120, $table))) {
        if ($data=$this->_db_fetch($result, MYSQL_ASSOC)) {
          if (!isset($data['Msg_text']) || strtolower($data['Msg_text'])!='ok') {
            // Repair table
            $this->_db_query($this->_db_makeQuery(125, $table));
          }
        }
        $this->_db_freeResult($result);
      }
      // Check overhead
      if ($result=$this->_db_query($this->_db_makeQuery(121, $table))) {
        if ($data=$this->_db_fetch($result, MYSQL_ASSOC)) {
          if (!empty($data['Data_free'])) {
            // Optimize table
            $this->_db_query($this->_db_makeQuery(126, $table));
          }
        }
        $this->_db_freeResult($result);
      }
    }
  }


 /**
  * Empty table
  * @return   boolean   TRUE on success or FALSE on error
  */
  function _db_emptyTable() {
    $result=false;
    $tbl=$this->_db_getTbl();
    if ($tbl!='') {
      if ($this->_db_query($this->_db_makeQuery(130, $tbl))) {
        $result=true;
      }
    }
    return $result;
  }


 /**
  * Create SQL query using a template
  * @param    int     $nr   Query template number
  * @param    mixed   ...   Unlimited query parameters. WARNING: scalar data types only!!!
  * @return   string  Created query
  */
  function _db_makeQuery($nr) {
    // Get method arguments
    $argv=func_get_args();
    // First argument is query template number (not needed)
    unset($argv[0]);
    // Load requested template
    $query='';
    switch($nr) {
      default   :   // Invalid query template number
                    // No query will be returned (empty string)
                    $query='';
      break;

      case  113 :   // Get tables list
                    // Used in: PCPIN_DB->_db_listTables()
                    $query='SHOW TABLES LIKE "'.PCPIN_DB_PREFIX.'_%"';
      break;

      case  114 :   // Retrieve table data
                    // Used in: PCPIN_DB->_db_readTableData()
                    $query='SHOW TABLE STATUS LIKE "\\_ARG1_\\"';
      break;

      case  115 :   // Retrieve table indexes
                    // Used in: PCPIN_DB->_db_readTableIndexes()
                    $query='SHOW INDEX FROM `\\_ARG1_\\`';
      break;

      case  120 :   // Check table
                    // Used in: PCPIN_DB->_db_cure()
                    $query='CHECK TABLE `\\_ARG1_\\`';
      break;

      case  121 :   // Get table status
                    // Used in: PCPIN_DB->_db_cure()
                    $query='SHOW TABLE STATUS LIKE "\\_arg1_\\"';
      break;

      case  125 :   // Repair table
                    // Used in: PCPIN_DB->_db_cure()
                    $query='REPAIR TABLE `\\_ARG1_\\`';
      break;

      case  126 :   // Optimize table
                    // Used in: PCPIN_DB->_db_cure()
                    $query='OPTIMIZE TABLE `\\_ARG1_\\`';
      break;

      case  130 :   // Empty table
                    // Used in: PCPIN_DB->_db_emptyTable()
                    $query='TRUNCATE TABLE `\\_ARG1_\\`';
      break;

      case 1000 :   // Get players with games count and times
                    // Used in: PCPIN_GameClient->listPlayers()
                    $where=' 1 ';
                    $order_by='';
                    $order_dir='ASC';
                    $limit='';
                    if (PCPIN_LIST_EXCLUDE_BOTS && empty($argv[7]) && empty($argv[8])) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (!empty($argv[1]) && is_array($argv[1]) && empty($argv[7]) && empty($argv[8])) {
                      $argv[1]=implode(',', $argv[1]);
                      $this->_db_prepareList($argv[1], true);
                      $where.=' AND `gc`.`guid` NOT IN('.$argv[1].')';
                    }
                    if (!empty($argv[2]) && is_array($argv[2])) {
                      $argv[2]=implode(',', $argv[2]);
                      $this->_db_prepareList($argv[2], true);
                      $where.=' AND `gc`.`guid` IN('.$argv[2].')';
                    }
                    if (!empty($argv[4]) && $argv[4]=='desc') {
                      $order_dir='DESC';
                    }
                    if (!empty($argv[3])) {
                      switch ($argv[3]) {

                        case 'name':
                          $order_by='ORDER BY `NameTxt` '.$order_dir;
                        break;

                        case 'kills':
                          $order_by='ORDER BY `KilledEnemies` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'deaths':
                          $order_by='ORDER BY `Deaths` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'rating':
                          $order_by='ORDER BY `Rating` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'kills_deaths':
                          $order_by='ORDER BY `K/D` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'kills_game':
                          $order_by='ORDER BY `K/G` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'kill_sprees':
                          $order_by='ORDER BY `KillSprees` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'death_sprees':
                          $order_by='ORDER BY `DeathSprees` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'games':
                          $order_by='ORDER BY `TotalGames` '.$order_dir.', `NameTxt` ASC';
                        break;

                        case 'time':
                          $order_by='ORDER BY `TimePlayed` '.$order_dir.', `NameTxt` ASC';
                        break;

                      }
                    }
                    if (!empty($argv[6])) {
                      $argv[5]*=1;
                      $argv[6]*=1;
                      $limit='LIMIT \\_arg5_\\, \\_arg6_\\';
                    }
                    if (!empty($argv[7])) {
                      $where.=' AND MD5( `gc`.`guid` ) = "\\_ARG7_\\"';
                    }
                    if (!empty($argv[8])) {
                      $where.=' AND `gc`.`game_id` = "\\_ARG8_\\"';
                    }
                    $query='SELECT `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`,
                                   `cn`.`name` AS `Name`,
                                   `cn`.`name_txt` AS `NameTxt`,
                                   SUM( IF( `gc`.`time_axis` > 0 OR `gc`.`time_allies` > 0, 1, 0 ) ) AS `PlayedGames`,
                                   SUM( 1 ) AS `TotalGames`,
                                   SUM( `gc`.`time_axis` ) + SUM( `gc`.`time_allies`) AS `TimePlayed`,
                                   SUM( `gc`.`time_axis` ) AS `TimeAxis`,
                                   SUM( `gc`.`time_allies` ) AS `TimeAllies`,
                                   SUM( `gc`.`time_spectator` ) AS `TimeSpectator`,
                                   SUM( `gc`.`time_axis` ) + SUM( `gc`.`time_allies`) + SUM( `gc`.`time_spectator` ) AS `TimeTotal`,
                                   SUM( `gc`.`killed_enemies` ) AS `KilledEnemies`,
                                   SUM( `gc`.`killed_teammates` ) AS `KilledTeammates`,
                                   SUM( `gc`.`killed_by_teammates` ) AS `KilledByTeammates`,
                                   SUM( `gc`.`killed_by_enemies` ) + SUM( `gc`.`killed_by_teammates` ) + SUM( `gc`.`killed_by_world` ) + SUM( `gc`.`suicides` ) AS `Deaths`,
                                   COALESCE( SUM( `gc`.`killed_enemies` ) / ( SUM( `gc`.`killed_by_enemies` ) + SUM( `gc`.`killed_by_teammates` ) + SUM( `gc`.`killed_by_world` ) + SUM( `gc`.`suicides` ) ), SUM( `gc`.`killed_enemies` ) ) AS `K/D`,
                                   SUM( `gc`.`killed_enemies` ) / COUNT( DISTINCT `gc`.`game_id` ) AS `K/G`,
                                   SUM( `gc`.`kill_sprees` ) AS `KillSprees`,
                                   MAX( `gc`.`longest_kill_spree` ) AS `LongestKillSpree`,
                                   SUM( `gc`.`death_sprees` ) AS `DeathSprees`,
                                   MAX( `gc`.`longest_death_spree` ) AS `LongestDeathSpree`,
                                   SUM( `gc`.`dynamite_plant` ) AS `DynamitePlantTotal`,
                                   SUM( `gc`.`dynamite_diffuse` ) AS `DynamiteDiffuseTotal`,
                                   SUM( `gc`.`repairs` ) AS `RepairsTotal`,
                                   SUM( `gc`.`disguises` ) AS `DisguisesTotal`,
                                   SUM( `gc`.`revives` ) AS `RevivesTotal`,
                                   SUM( `gc`.`objectives_taken` ) AS `ObjectivesTakenTotal`,
                                   SUM( `gc`.`health_given` ) AS `HealthGivenTotal`,
                                   SUM( `gc`.`health_taken` ) AS `HealthTakenTotal`,
                                   SUM( `gc`.`ammo_given` ) AS `AmmoGivenTotal`,
                                   SUM( `gc`.`ammo_taken` ) AS `AmmoTakenTotal`,
                                   SUM( IF( `gc`.`first_kill` = "y", 1, 0 ) ) AS `FirstKillTotal`,
                                   SUM( IF( `gc`.`first_death` = "y", 1, 0 ) ) AS `FirstDeathTotal`,

                                   SUM( `gc`.`killed_enemies` ) * '.RATING_FACTOR_KILL.'
                                   + SUM( IF( `gc`.`first_kill` = "y", 1, 0 ) ) * '.RATING_FACTOR_FIRST_KILL.'
                                   + SUM( `gc`.`kill_sprees` ) * '.RATING_FACTOR_KILL_SPREE.'
                                   + SUM( `gc`.`objectives_taken` ) * '.RATING_FACTOR_OBJECTIVES_TAKEN.'
                                   + ( SUM( `gc`.`time_axis` ) + SUM( `gc`.`time_allies`) ) * '.RATING_FACTOR_SECONDS_PLAYED.'
                                   + SUM( `gc`.`revives` ) * '.RATING_FACTOR_REVIVE.'
                                   + SUM( `gc`.`health_given` ) * '.RATING_FACTOR_HEALTH_PACK.'
                                   + SUM( `gc`.`ammo_given` ) * '.RATING_FACTOR_AMMO_PACK.'
                                   + SUM( `gc`.`dynamite_plant` ) * '.RATING_FACTOR_DYNAMITE_PLANT.'
                                   + SUM( `gc`.`dynamite_diffuse` ) * '.RATING_FACTOR_DYNAMITE_DIFFUSE.'
                                   + SUM( `gc`.`repairs` ) * '.RATING_FACTOR_REPAIR.'
                                   + SUM( `gc`.`disguises` ) * '.RATING_FACTOR_DISGUISE.'

                                   + SUM( `gc`.`killed_by_enemies` ) * '.RATING_FACTOR_KILLED_BY_ENEMY.'
                                   + SUM( IF( `gc`.`first_death` = "y", 1, 0 ) ) * '.RATING_FACTOR_FIRST_DEATH.'
                                   + SUM( `gc`.`killed_by_teammates` ) * '.RATING_FACTOR_KILLED_BY_TEAMMATE.'
                                   + SUM( `gc`.`killed_by_world` ) * '.RATING_FACTOR_KILLED_BY_WORLD.'
                                   + SUM( `gc`.`suicides` ) * '.RATING_FACTOR_KILLED_BY_WORLD.'
                                   + SUM( `gc`.`death_sprees` ) * '.RATING_FACTOR_DEATH_SPREE.'
                                   + SUM( `gc`.`killed_teammates` ) * '.RATING_FACTOR_TEAMKILL.'
                                   AS `Rating`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                                   LEFT JOIN `'.PCPIN_DB_PREFIX.'game` `ga` ON `ga`.`id` = `gc`.`game_id`
                                   LEFT JOIN `'.PCPIN_DB_PREFIX.'clientname` `cn` ON `cn`.`guid` = `gc`.`guid` AND `cn`.`last_used` = "y"
                                   LEFT JOIN `'.PCPIN_DB_PREFIX.'gameclientdamage` `gcd` ON `gcd`.`guid` = `gc`.`guid`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                                   '.$order_by.'
                                   '.$limit.'
                                   ';
      break;

      case 1005 :   // Get total players number passed to search query used in case 1000
                    // Used in: PCPIN_GameClient->listPlayers()
                    $where=' 1 ';
                    if (PCPIN_LIST_EXCLUDE_BOTS && empty($argv[7]) && empty($argv[8])) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%" ';
                    }
                    if (!empty($argv[1]) && is_array($argv[1]) && empty($argv[7]) && empty($argv[8])) {
                      $argv[1]=implode(',', $argv[1]);
                      $this->_db_prepareList($argv[1], true);
                      $where.=' AND `gc`.`guid` NOT IN('.$argv[1].')';
                    }
                    if (!empty($argv[2]) && is_array($argv[2])) {
                      $argv[2]=implode(',', $argv[2]);
                      $this->_db_prepareList($argv[2], true);
                      $where.=' AND `gc`.`guid` IN('.$argv[2].')';
                    }
                    if (!empty($argv[7])) {
                      $where.=' AND MD5( `gc`.`guid` ) = "\\_ARG7_\\"';
                    }
                    if (!empty($argv[8])) {
                      $where.=' AND `gc`.`game_id` = "\\_ARG8_\\"';
                    }
                    $query='SELECT COUNT( DISTINCT `gc`.`guid` ) AS `TotalResults`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where;
      break;

      case 1020 :   // Get names used by client
                    // Used in: PCPIN_ClientName->getUsedNames()
                    $query='SELECT `cn`.`name` AS `Name`,
                                   `cn`.`name_txt` AS `NameTxt`,
                                   SUM( 1 ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'clientname` `cn`
                             WHERE `cn`.`guid` = "\\_ARG1_\\"
                          GROUP BY `cn`.`name`
                          ORDER BY `Count` DESC';
      break;

      case 1030 :   // Get average ping
                    // Used in: PCPIN_GameClient->getAvgPing()
                    $where=' 1 ';
                    if (!empty($argv[2])) {
                      $where.=' AND `gc`.`game_id` = "\\_ARG2_\\"';
                    }
                    $query='SELECT COALESCE( AVG( `gc`.`ping` ), 0 ) AS `AvgPing`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                                   AND `gc`.`guid` = "\\_ARG1_\\"
                                   AND `gc`.`ping` > 0';
      break;

      case 1040 :   // Get played teams/classes
                    // Used in: PCPIN_GameClientTeamClass->getPlayedClasses()
                    $query='SELECT `class` AS `Class`,
                                   SUM( IF( `team` != 3, `time`, 0 ) ) AS `TimeTotal`,
                                   SUM( IF( `team` = 1, `time`, 0 ) ) AS `TimeAxis`,
                                   SUM( IF( `team` = 2, `time`, 0 ) ) AS `TimeAllies`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientteamclass`
                             WHERE `guid` = "\\_ARG1_\\"
                          GROUP BY `class`
                          ORDER BY `TimeTotal` DESC';
      break;

      case 1050 :   // Get weapon stats
                    // Used in: PCPIN_GameClientWS->getWeaponStats()
                    $query='SELECT `weapon` AS `Weapon`,
                                   SUM( `hits` ) AS `Hits`,
                                   SUM( `shots` ) AS `Shots`,
                                   SUM( `kills` ) AS `Kills`,
                                   SUM( `deaths` ) AS `Deaths`,
                                   SUM( `headshots` ) AS `Headshots`,
                                   COALESCE( SUM( `hits` ) / SUM( `shots` ) * 100, 0 ) AS `Accuracy`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientws`
                             WHERE `guid` = "\\_ARG1_\\"
                          GROUP BY `weapon`
                          ORDER BY `Kills` DESC';
      break;

      case 1060 :   // Get suicides grouped by weapon
                    // Used in: PCPIN_GameClientSuicide->getSuicidesWeapon()
                    $where=' 1 ';
                    if (!empty($argv[2])) {
                      $where.=' AND `game_id` = "\\_ARG2_\\"';
                    }
                    $query='SELECT `mod` AS `MOD`,
                                   SUM( `count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientsuicide`
                             WHERE '.$where.'
                                   AND `guid` = "\\_ARG1_\\"
                          GROUP BY `mod`';
      break;

      case 1065 :   // Get suicides grouped by guid, weapon and team
                    // Used in: PCPIN_GameClientSuicide->getSuicidesWeaponTeam()
                    $query='SELECT `guid` AS `GUID`,
                                   `mod` AS `MOD`,
                                   `team` AS `Team`,
                                   SUM( `count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientsuicide`
                             WHERE `game_id` = "\\_ARG1_\\"
                          GROUP BY `guid`, `mod`, `team`';
      break;

      case 1070 :   // Get kill stats grouped ny MOD
                    // Used in: PCPIN_GameClientKill->getKillsMOD()
                    $where=' `guid` = "\\_ARG1_\\"';
                    if (!empty($argv[2]) && is_array($argv[2])) {
                      $argv[2]=implode(',', $argv[2]);
                      $this->_db_prepareList($argv[2], true);
                      $where.=' AND `mod` NOT IN('.$argv[2].')';
                    }
                    if (!empty($argv[3])) {
                      $where.=' AND `game_id` = "\\_ARG3_\\"';
                    }
                    $query='SELECT `mod` AS `MOD`,
                                   SUM( IF( `enemy` = "y", `count`, 0 ) ) AS `Kills`,
                                   SUM( IF( `enemy` = "n", `count`, 0 ) ) AS `Teamkills`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill`
                             WHERE '.$where.'
                          GROUP BY `mod`
                          ORDER BY `Kills` DESC';
      break;

      case 1075 :   // Get kill stats grouped by guid, weapon and team
                    // Used in: PCPIN_GameClientKill->getKillsGuidTeamMod()
                    $query='SELECT `guid` AS `GUID`,
                                   `mod` AS `MOD`,
                                   `team` AS `Team`,
                                   SUM( IF( `enemy` = "y", `count`, 0 ) ) AS `Kills`,
                                   SUM( IF( `enemy` = "n", `count`, 0 ) ) AS `Teamkills`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill`
                             WHERE `game_id` = "\\_ARG1_\\"
                          GROUP BY `guid`, `mod`, `team`';
      break;

      case 1080 :   // Get death stats grouped ny MOD
                    // Used in: PCPIN_GameClientDeath->getDeathsMOD()
                    $where=' `guid` = "\\_ARG1_\\"';
                    if (!empty($argv[2]) && is_array($argv[2])) {
                      $argv[2]=implode(',', $argv[2]);
                      $this->_db_prepareList($argv[2], true);
                      $where.=' AND `mod` NOT IN('.$argv[2].')';
                    }
                    if (!empty($argv[3])) {
                      $where.=' AND `game_id` = "\\_ARG3_\\"';
                    }
                    $query='SELECT `mod` AS `MOD`,
                                   SUM( IF( `enemy` = "y" OR `enemy` = "w", `count`, 0 ) ) AS `Deaths`,
                                   SUM( IF( `enemy` = "n", `count`, 0 ) ) AS `Deamdeaths`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientdeath`
                             WHERE '.$where.'
                          GROUP BY `mod`';
      break;

      case 1085 :   // Get kill stats grouped by guid, weapon and team
                    // Used in: PCPIN_GameClientDeath->getDeathsGuidTeamMod()
                    $query='SELECT `guid` AS `GUID`,
                                   `mod` AS `MOD`,
                                   `team` AS `Team`,
                                   SUM( IF( `enemy` = "y" OR `enemy` = "w", `count`, 0 ) ) AS `Deaths`,
                                   SUM( IF( `enemy` = "n", `count`, 0 ) ) AS `Teamdeaths`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientdeath`
                             WHERE `game_id` = "\\_ARG1_\\"
                          GROUP BY `guid`, `mod`, `team`';
      break;

      case 1090 :   // Get top N "favorite victims"
                    // Used in: PCPIN_GameClientKill->getFavoriteVictims()
                    $where=' `gck`.`guid` = "\\_ARG1_\\"';
                    $limit='';
                    if (!empty($argv[2])) {
                      $argv[2]*=1;
                      $limit=' LIMIT \\_ARG2_\\';
                    }
                    $query='SELECT `gck`.`victim_guid` AS `VictimGUID`,
                                   MD5( `gck`.`victim_guid` ) AS `VictimGUID_Key`,
                                   `cn`.`name` AS `VictimName`,
                                   `cn`.`name_txt` AS `VictimNameTxt`,
                                   COALESCE( SUM( `gck`.`count` ), 0 ) AS `Kills`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill` `gck`
                                   LEFT JOIN `'.PCPIN_DB_PREFIX.'clientname` `cn` ON `cn`.`guid` = `gck`.`victim_guid` AND `cn`.`last_used` = "y"
                             WHERE '.$where.'
                          GROUP BY `gck`.`victim_guid`
                          ORDER BY `Kills` DESC
                          '.$limit;
      break;

      case 1100 :   // Get top N worst enemies
                    // Used in: PCPIN_GameClientKill->getWorstEnemies()
                    $where=' `gck`.`victim_guid` = "\\_ARG1_\\"';
                    $limit='';
                    if (!empty($argv[2])) {
                      $argv[2]*=1;
                      $limit=' LIMIT \\_ARG2_\\';
                    }
                    $query='SELECT `gck`.`guid` AS `KillerGUID`,
                                   MD5( `gck`.`guid` ) AS `KillerGUID_Key`,
                                   `cn`.`name` AS `KillerName`,
                                   `cn`.`name_txt` AS `KillerNameTxt`,
                                   COALESCE( SUM( `gck`.`count` ), 0 ) AS `Deaths`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill` `gck`
                                   LEFT JOIN `'.PCPIN_DB_PREFIX.'clientname` `cn` ON `cn`.`guid` = `gck`.`guid` AND `cn`.`last_used` = "y"
                             WHERE '.$where.'
                          GROUP BY `gck`.`guid`
                          ORDER BY `Deaths` DESC
                          '.$limit;
      break;

      case 1110 :   // Count how many times one player killed another player
                    // Used in: PCPIN_GameClientKill->countKillsByGuid()
                    $query='SELECT COALESCE( SUM( `gck`.`count` ), 0 ) AS `Kills`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill` `gck`
                             WHERE `gck`.`guid` = "\\_ARG1_\\"
                                   AND `gck`.`victim_guid` = "\\_ARG2_\\"';
      break;

      case 1120 :   // Get kills count grouped by class
                    // Used in: PCPIN_GameClientKill->countKillsByTeamClass()
                    $where=' 1 ';
                    if (isset($argv[2])) {
                      $where.=' AND `gck`.`team` = "\\_ARG2_\\"';
                    }
                    if (isset($argv[3])) {
                      $where.=' AND `gck`.`class` = "\\_ARG3_\\"';
                    }
                    $query='SELECT `gck`.`team` AS `Team`,
                                   `gck`.`class` AS `Class`,
                                   `gck`.`mod` AS `MOD`,
                                   SUM( `gck`.`count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill` `gck`
                             WHERE '.$where.'
                                   AND `gck`.`guid` = "\\_ARG1_\\"
                                   AND `gck`.`enemy` = "y"
                          GROUP BY `gck`.`team`, `gck`.`class`, `gck`.`mod`
                          ORDER BY `Count` DESC';
      break;

      case 1125 :   // Get kills for all players for specified game
                    // Used in: PCPIN_GameClientKill->getKillsByTeam()
                    $query='SELECT `gck`.`guid` AS `GUID`,
                                   `gck`.`team` AS `Team`,
                                   `gck`.`class` AS `Class`,
                                   `gck`.`mod` AS `MOD`,
                                   SUM( IF( `gck`.`enemy` = "y", `gck`.`count`, 0 ) ) AS `KilledEnemies`,
                                   SUM( IF( `gck`.`enemy` = "n", `gck`.`count`, 0 ) ) AS `KilledTeammates`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill` `gck`
                             WHERE `gck`.`game_id` = "\\_ARG1_\\"
                          GROUP BY `gck`.`guid`, `gck`.`team`, `gck`.`class`, `gck`.`mod`';
      break;

      case 1127 :   // Get deaths for all players for specified game
                    // Used in: PCPIN_GameClientDeath->getDeathsByTeam()
                    $query='SELECT `gcd`.`guid` AS `GUID`,
                                   `gcd`.`team` AS `Team`,
                                   SUM( IF( `gcd`.`enemy` = "y", `gcd`.`count`, 0 ) ) AS `KilledByEnemies`,
                                   SUM( IF( `gcd`.`enemy` = "n", `gcd`.`count`, 0 ) ) AS `KilledByTeammates`,
                                   SUM( IF( `gcd`.`enemy` = "w", `gcd`.`count`, 0 ) ) AS `KilledByWorld`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientdeath` `gcd`
                             WHERE `gcd`.`game_id` = "\\_ARG1_\\"
                          GROUP BY `gcd`.`guid`, `gcd`.`team`';
      break;

      case 1130 :   // Get games list
                    // Used in: PCPIN_Game->listGames()
                    $where=' 1 ';
                    $order_by='';
                    $order_dir='ASC';
                    $limit='';
                    if (!empty($argv[2]) && $argv[2]=='desc') {
                      $order_dir='DESC';
                    }
                    if (!empty($argv[1])) {
                      switch ($argv[1]) {

                        case 'nr':
                          $order_by='ORDER BY `GameNr` '.$order_dir;
                        break;

                        case 'map':
                          $order_by='ORDER BY `MapName` '.$order_dir.', `GameNr` ASC';
                        break;

                        case 'players':
                          $order_by='ORDER BY `Players` '.$order_dir.', `GameNr` ASC';
                        break;

                        case 'clients':
                          $order_by='ORDER BY `Clients` '.$order_dir.', `GameNr` ASC';
                        break;

                        case 'duration':
                          $order_by='ORDER BY `Duration` '.$order_dir.', `GameNr` ASC';
                        break;

                      }
                    }
                    if (!empty($argv[4])) {
                      $argv[3]*=1;
                      $argv[4]*=1;
                      $limit='LIMIT \\_arg3_\\, \\_arg4_\\';
                    }
                    if (!empty($argv[5])) {
                      $where.=' AND `ga`.`id` = "\\_ARG5_\\"';
                    }
                    if (!empty($argv[6])) {
                      $where.=' AND `gc`.`guid` = "\\_ARG6_\\"';
                    }
                    $query='SELECT
                                   `ga`.`id` AS `GameNr`,
                                   `ga`.`mapname` AS `MapName`,
                                   `ga`.`duration` AS `Duration`,
                                   `ga`.`players` AS `Players`,
                                   `ga`.`clients` AS `Clients`,
                                   SUM( IF( `gc`.`time_axis` > 0, 1, 0 ) ) AS `PlayersAxis`,
                                   SUM( IF( `gc`.`time_allies` > 0, 1, 0 ) ) AS `PlayersAllies`,
                                   SUM( IF( `gc`.`time_spectator` > 0, 1, 0 ) ) AS `Spectators`,
                                   COALESCE( ROUND( AVG( IF( `gc`.`time_axis` > 0 AND `gc`.`ping` > 0, `gc`.`ping`, NULL ) ) ), 0 ) AS `AvgPingAxis`,
                                   COALESCE( ROUND( AVG( IF( `gc`.`time_allies` > 0 AND `gc`.`ping` > 0, `gc`.`ping`, NULL ) ) ), 0 ) AS `AvgPingAllies`,
                                   COALESCE( ROUND( AVG( IF( `gc`.`time_spectator` > 0 AND `gc`.`ping` > 0, `gc`.`ping`, NULL ) ) ), 0 ) AS `AvgPingSpectators`
                                   FROM `'.PCPIN_DB_PREFIX.'game` `ga`
                                        LEFT JOIN `'.PCPIN_DB_PREFIX.'gameclient` `gc` ON `gc`.`game_id` = `ga`.`id`
                             WHERE '.$where.'
                          GROUP BY `ga`.`id`
                                   '.$order_by.'
                                   '.$limit.'
                                   ';
      break;

      case 1135 :   // Count results to be returned by query 1030
                    // Used in: PCPIN_Game->listGames()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `ga`.`id` = "\\_ARG1_\\"';
                    }
                    if (!empty($argv[2])) {
                      $where.=' AND `gc`.`guid` = "\\_ARG2_\\"';
                    }
                    $query='SELECT COUNT( DISTINCT `ga`.`id` ) AS `TotalResults`
                                   FROM `'.PCPIN_DB_PREFIX.'game` `ga`
                                        LEFT JOIN `'.PCPIN_DB_PREFIX.'gameclient` `gc` ON `gc`.`game_id` = `ga`.`id`
                             WHERE '.$where;
      break;

      case 1140 :   // Get world death for specified game count grouped by GUID team and MOD
                    // Used in: PCPIN_GameClientDeath->getWorldDeathsTeamMOD()
                    $query='SELECT `guid` AS `GUID`,
                                   `team` AS `Team`,
                                   `mod` AS `MOD`,
                                   SUM( `count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientdeath`
                             WHERE `game_id` = "\\_ARG1_\\"
                                   AND `enemy` = "w"
                          GROUP BY `guid`, `team`, `mod`';
      break;

      case 1150 :   // Get misc stats for specified game count grouped by GUID and team
                    // Used in: PCPIN_GameClientMisc->getMiscTeam()
                    $query='SELECT `guid` AS `GUID`,
                                   `team` AS `Team`,
                                   SUM( `dynamite_plant` ) AS `DynamitesPlanted`,
                                   SUM( `dynamite_diffuse` ) AS `DynamitesDiffused`,
                                   SUM( `repairs` ) AS `Repairs`,
                                   SUM( `disguises` ) AS `Disguises`,
                                   SUM( `objectives_taken` ) AS `ObjectivesTaken`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientmisc`
                             WHERE `game_id` = "\\_ARG1_\\"
                          GROUP BY `guid`, `team`';
      break;

      case 1160 :   // Get revives made by all players for specified game
                    // Used in: PCPIN_GameClientRevive->getRevivesByTeam()
                    $query='SELECT `gcr`.`guid` AS `GUID`,
                                   `gcr`.`team` AS `Team`,
                                   SUM( `gcr`.`count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientrevive` `gcr`
                             WHERE `gcr`.`game_id` = "\\_ARG1_\\"
                          GROUP BY `gcr`.`guid`, `gcr`.`team`';
      break;

      case 1170 :   // Get ammo packs given by all players for specified game
                    // Used in: PCPIN_GameClientAmmo->getAmmoByTeam()
                    $query='SELECT `gca`.`guid` AS `GUID`,
                                   `gca`.`team` AS `Team`,
                                   SUM( `gca`.`count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientammo` `gca`
                             WHERE `gca`.`game_id` = "\\_ARG1_\\"
                          GROUP BY `gca`.`guid`, `gca`.`team`';
      break;

      case 1180 :   // Get health packs given by all players for specified game
                    // Used in: PCPIN_GameClientHealth->getHealthByTeam()
                    $query='SELECT `gch`.`guid` AS `GUID`,
                                   `gch`.`team` AS `Team`,
                                   SUM( `gch`.`count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclienthealth` `gch`
                             WHERE `gch`.`game_id` = "\\_ARG1_\\"
                          GROUP BY `gch`.`guid`, `gch`.`team`';
      break;

      case 1190 :   // Get deaths count for all weapons
                    // Used in: PCPIN_GameClientDeath->countDeathsByMod()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT `mod` AS `MOD`,
                                   SUM( `count` ) AS `Count`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientdeath`
                             WHERE '.$where.'
                          GROUP BY `mod`
                          ORDER BY `Count` DESC
                          ';
      break;

      case 1200 :   // Get GUIDKey+Name and kills number of the player who made most kills with specified MOD
                    // Used in: PCPIN_GameClientKill->getBestModKiller()
                    $where=' 1 ';
                    $argv[1]=implode(',', $argv[1]);
                    $this->_db_prepareList($argv[1], true);
                    if (!empty($argv[2])) {
                      $where.=' AND `gck`.`game_id` >= "\\_ARG2_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gck`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=@PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gck`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gck`.`count` ) AS `Count`,
                                   `gck`.`guid` AS `GUID`,
                                   MD5( `gck`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientkill` `gck`
                             WHERE '.$where.'
                                   AND `gck`.`mod` IN('.$argv[1].')
                          GROUP BY `gck`.`mod`, `gck`.`guid`
                          ORDER BY `Count` DESC
                             LIMIT 1';
      break;

      case 1210 :   // Get GUIDKey+Name and deaths number of the player who got most deaths with specified MOD
                    // Used in: PCPIN_GameClientDeath->getBestModVictim()
                    $where=' 1 ';
                    if (!empty($argv[2])) {
                      $where.=' AND `gcd`.`game_id` >= "\\_ARG2_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gcd`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gcd`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gcd`.`count` ) AS `Count`,
                                   `gcd`.`guid` AS `GUID`,
                                   MD5( `gcd`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientdeath` `gcd`
                             WHERE '.$where.'
                                   AND `gcd`.`mod` = "\\_ARG1_\\"
                          GROUP BY `gcd`.`mod`, `gcd`.`guid`
                          ORDER BY `Count` DESC
                             LIMIT 1';
      break;

      case 1220 :   // Get GUIDKey+Name and kills number of the player who achieved the longest kill spree
                    // Used in: PCPIN_GameClient->getLongestDeathSpree()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT MAX( `gc`.`longest_death_spree` ) AS `Count`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `Count` DESC
                             LIMIT 1';
      break;

      case 1222 :   // Get GUIDKey+Name and kills number of the player who achieved the longest death spree
                    // Used in: PCPIN_GameClient->getLongestKillSpree()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT MAX( `gc`.`longest_kill_spree` ) AS `Count`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `Count` DESC
                             LIMIT 1';
      break;

      case 1225 :   // Get GUIDKey+Name and kills number of the player who achieved the most kill sprees
                    // Used in: PCPIN_GameClient->getMostKillSprees()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gc`.`kill_sprees` ) AS `Count`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `Count` DESC
                             LIMIT 1';
      break;

      case 1230 :   // Get GUIDKey+Name and bounties number of the player who collected most bounty points
                    // Used in: PCPIN_GameClient->getBestBountyHunter()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gc`.`bounties` ) AS `Count`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `Count` DESC
                             LIMIT 1';
      break;

      case 1240 :   // Get player with the best Kills/Deaths ratio
                    // Used in: PCPIN_GameClient->getBestKD()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT COALESCE( SUM( `gc`.`killed_enemies` ) / ( SUM( `gc`.`killed_by_enemies` ) + SUM( `gc`.`killed_by_teammates` ) + SUM( `gc`.`killed_by_world` ) + SUM( `gc`.`suicides` ) ), SUM( `gc`.`killed_enemies` ) ) AS `K/D`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `K/D` DESC
                             LIMIT 1';
      break;

      case 1250 :   // Get player with the best Kills/Deaths ratio
                    // Used in: PCPIN_GameClient->getBestKD()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gc`.`killed_enemies` ) / COUNT( DISTINCT `gc`.`game_id` ) AS `K/G`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `K/G` DESC
                             LIMIT 1';
      break;

      case 1260 :   // Get player with most world deaths number
                    // Used in: PCPIN_GameClientDeath->getMostWorldDeaths()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gcd`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gcd`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gcd`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gcd`.`count` ) AS `Count`,
                                   `gcd`.`guid` AS `GUID`,
                                   MD5( `gcd`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclientdeath` `gcd`
                             WHERE '.$where.'
                                   AND `gcd`.`enemy` = "w"
                          GROUP BY `gcd`.`guid`
                          ORDER BY `Count` DESC
                             LIMIT 1';
      break;

      case 1270 :   // Get "best medic" player
                    // Used in: PCPIN_GameClient->getBestMedic()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=@PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gc`.`revives` ) AS `Revives`,
                                   SUM( `gc`.`health_given` ) AS `HealthGiven`,
                                     SUM( `gc`.`revives` ) * '.AWARDS_FACTOR_REVIVE.'
                                   + SUM( `gc`.`health_given` ) * '.AWARDS_FACTOR_HEALTH_PACK.' AS `ClassScore`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `ClassScore` DESC
                             LIMIT 1';
      break;

      case 1280 :   // Get "best engineer" player
                    // Used in: PCPIN_GameClient->getBestEngineer()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=@PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gc`.`dynamite_plant` ) AS `DynamitePlants`,
                                   SUM( `gc`.`dynamite_diffuse` ) AS `DynamiteDiffuses`,
                                   SUM( `gc`.`repairs` ) AS `Repairs`,
                                   SUM( `gc`.`engineer_kills` ) AS `EngineerKills`,
                                     SUM( `gc`.`dynamite_plant` ) * '.AWARDS_FACTOR_DYNAMITE_PLANT.'
                                   + SUM( `gc`.`dynamite_diffuse` ) * '.AWARDS_FACTOR_DYNAMITE_DIFFUSE.'
                                   + SUM( `gc`.`repairs` ) * '.AWARDS_FACTOR_REPAIR.'
                                   + SUM( `gc`.`engineer_kills` ) * '.AWARDS_FACTOR_ENGINEER_KILL.' AS `ClassScore`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `ClassScore` DESC
                             LIMIT 1';
      break;

      case 1290 :   // Get "best field op" player
                    // Used in: PCPIN_GameClient->getBestFieldOp()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=@PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gc`.`ammo_given` ) AS `AmmoGiven`,
                                   SUM( `gc`.`fieldops_kills` ) AS `FieldOpKills`,
                                     SUM( `gc`.`ammo_given` ) * '.AWARDS_FACTOR_AMMO_GIVEN.'
                                   + SUM( `gc`.`fieldops_kills` ) * '.AWARDS_FACTOR_FIELDOP_KILL.' AS `ClassScore`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `ClassScore` DESC
                             LIMIT 1';
      break;

      case 1300 :   // Get "best covert op" player
                    // Used in: PCPIN_GameClient->getBestCovertOp()
                    $where=' 1 ';
                    if (!empty($argv[1])) {
                      $where.=' AND `gc`.`game_id` >= "\\_ARG1_\\"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_BOTS) {
                      $where.=' AND `gc`.`guid` NOT LIKE BINARY "OMNIBOT%"';
                    }
                    if (@PCPIN_LIST_EXCLUDE_GUIDS!='') {
                      $exclude_guids=@PCPIN_LIST_EXCLUDE_GUIDS;
                      $this->_db_prepareList($exclude_guids, true);
                      $where.=' AND `gc`.`guid` NOT IN( '.$exclude_guids.' )';
                    }
                    $query='SELECT SUM( `gc`.`disguises` ) AS `Disguises`,
                                   SUM( `gc`.`covertops_kills` ) AS `CovertOpKills`,
                                     SUM( `gc`.`disguises` ) * '.AWARDS_FACTOR_DISGUISE.'
                                   + SUM( `gc`.`covertops_kills` ) * '.AWARDS_FACTOR_COVERTOP_KILL.' AS `ClassScore`,
                                   `gc`.`guid` AS `GUID`,
                                   MD5( `gc`.`guid` ) AS `GUID_Key`
                              FROM `'.PCPIN_DB_PREFIX.'gameclient` `gc`
                             WHERE '.$where.'
                          GROUP BY `gc`.`guid`
                          ORDER BY `ClassScore` DESC
                             LIMIT 1';
      break;

      case 1310 :   // Delete all records from "excludeguids" table
                    // Used in: PCPIN_ExcludeGUIDs->deleteAllGUIDs()
                    $query='TRUNCATE TABLE `'.PCPIN_DB_PREFIX.'excludeguids`';
      break;

    }
    if ($query!='') {
      $query=str_replace('\\_', PCPIN_SQLQUERY_ARG_SEPARATOR_START, str_replace('_\\', PCPIN_SQLQUERY_ARG_SEPARATOR_END, $query));
      foreach ($argv as $key=>$arg) {
        // Escape dangerous characters from query parameters
        if (is_scalar($arg)) {
          // Scalar argument
          $arg_with_wildcards=$this->_db_escapeStr($arg, false);
          $arg_no_wildcards=$this->_db_escapeStr($arg);
          // Pass parameters to query template
          $query=str_replace(PCPIN_SQLQUERY_ARG_SEPARATOR_START.'ARG'.$key.PCPIN_SQLQUERY_ARG_SEPARATOR_END, $arg_with_wildcards, $query);
          $query=str_replace(PCPIN_SQLQUERY_ARG_SEPARATOR_START.'arg'.$key.PCPIN_SQLQUERY_ARG_SEPARATOR_END, $arg_no_wildcards, $query);
        } else {
          // Invalid argument type. An empty string will be assumed.
          $query=str_replace(PCPIN_SQLQUERY_ARG_SEPARATOR_START.'ARG'.$key.PCPIN_SQLQUERY_ARG_SEPARATOR_END, '""', $query);
          $query=str_replace(PCPIN_SQLQUERY_ARG_SEPARATOR_START.'arg'.$key.PCPIN_SQLQUERY_ARG_SEPARATOR_END, '""', $query);
        }
      }
    }
    return $query;
  }

}
?>